痾其實我也不知道會不會完賽,升上研究所之後變得好忙...
在寫套件之前,我們要知道自己要把什麼東西「造出來」、用什麼工具「把它運到瀏覽器」,以及前端如何「優雅地使用它」。所以我不會先講很多 Rust 語法;先理解輸出形態與 toolchain,之後所有選擇都會明朗而省時。
Rust 要跑進瀏覽器,最終必須變成 WebAssembly,並透過一層 JavaScript glue 被前端使用。這裡有三個面向需要去釐清:
產物長相
你的 crate 會輸出成一個 .wasm 檔與相對應的 JS shim(由 wasm-bindgen 生成),還可能附帶 .d.ts 型別定義。
如何被前端載入
現代前端以 ESM 為主。你需要讓 bundler 或 runtime 知道要如何 import 你的封裝、去哪裡找到 .wasm、要不要在 Web Worker 跑。
封裝與發佈
面向 npm 的套件,要處理 package.json 的 exports map、型別檔、瀏覽器與 Node 的差異入口,以及效能/體積的平衡。
把這三件事想清楚,後面的 lib 與 cdylib、workspace、wasm-pack、Vite/Next 只是各自站在不同關卡的小幫手。
Rust Code → 編成 WebAssembly → 由 wasm-bindgen 生成 JS/TS API → 交給前端 bundler 或 runtime 透過 ESM 載入與使用。
Rust crate (lib)
└─ cargo build --target wasm32-unknown-unknown
└─ .wasm 產物
└─ wasm-bindgen/wasm-pack 生成 JS glue + d.ts
└─ 前端 (Vite/Next) 以 ESM import
└─ 瀏覽器 / Web Worker 執行
檢查命令
rustup --version
cargo --version
rustup target list --installed
wasm-pack --version
node -v
pnpm -v
建議目標平台安裝
rustup target add wasm32-unknown-unknow
知道到這裡即可下面是一些更詳細的講解(-0-)
lib 與 cdylib 的差別:
這邊我會建議用 workspace 分成 core 與 wasm 兩個 crate:core(lib)純邏輯、零平台耦合、可複用、可測試。然後 wasm(cdylib)專責與 JS 溝通(如 wasm-bindgen 標註、型別包裝、錯誤轉譯)。這樣做可以把演算法和平台邊界解耦,架構會更清楚明瞭。
wasm-bindgen 跟 wasm-pack 分別是什麼:
常見工作流程
前端體驗的關鍵在 package.json 的設計:
為什麼重要?如果沒有 exports map,使用者的 bundler 可能找不到真正入口或把內部檔案誤當公開 API;沒有 d.ts,TS user experience 會大打折扣;而沒有明確 ESM,會在 Node 與瀏覽器間出現相容問題。
當你的運算很重、會阻塞 UI 主緒時,就把 wasm 跑到 Worker,而我們要做的套件運算都偏大所以會用到 worker。
所以是這樣安排的:
[crate: core (lib)] [crate: wasm (cdylib + wasm-bindgen)]
│ │
└── 演算法/邏輯 ────► │ API、錯誤轉譯
│
cargo build --target wasm32-unknown-unknown
│
wasm-bindgen / wasm-pack
│
生成 pkg/: index.js + package.json + .wasm + .d.ts
│
前端 (Vite/Next) 以 ESM import
│
瀏覽器主緒 或 Web Worker 實際執行與渲染
今天釐清了一些名詞與基本概念,名詞如下:crate、workspace、lib/cdylib、wasm-bindgen、wasm-pack、ESM、Web Worker、exports map、d.ts 。以下是三個小問題,希望讓大家檢驗有沒有在文章內學到東西:
lib 與 cdylib 的差別
lib 是一般函式庫,偏邏輯內聚;cdylib 用於跨語言邊界的產物,搭 wasm-bindgen 才能讓 JS 直接用。實務上常採 lib + cdylib 分層:lib 放核心邏輯、cdylib 專注 JS 介面。
workspace 的用途
把多個 crate 放在一個倉庫中共享版本、鎖檔與工具鏈:可把核心邏輯與平台邊界切開;之後你想同時輸出「瀏覽器版 wasm」和「Node 原生擴充」也更好管理。
exports map 為何重要
它決定「使用者 import 你的套件時」實際拿到哪個檔案、在哪個環境下走哪個入口,並避免外部依賴你的內部檔案路徑。這是維持 API 穩定與 DX 的關鍵。
在規劃的時候覺得還好,真的寫出來快瘋掉,好好笑不能確保之後的內容還會這麼多真的是在搞死自己,唸的人也很痛苦我很抱歉。
寫得非常詳細!Rust + WASM 的架構說明很清楚,特別是 lib vs cdylib 和 workspace 的解釋對初學者很有幫助。期待後續的實作內容!
另外也懇請大家有空的話可以訂閱我的系列「南桃AI重生記」,一起互相交流學習!加油!
版主您好!看到您的開頭,身為同樣身處忙碌中的人,非常能理解您在研究所課業與完賽之間的掙扎,請您量力而為,健康最重要!
這篇文章的切入點非常棒,從「輸出形態與 toolchain」開始講解,確實能讓讀者更清晰地理解 Rust 與前端整合的整體脈絡,避免一開始就被語法細節困住。您對「產物長相」、「前端載入」和「封裝發佈」這三面向的拆解非常到位,尤其是簡潔的心智圖和名詞介紹,對於想入門 Rust/Wasm 的開發者來說,提供了非常有價值的基礎。
關於 lib
與 cdylib
搭配 workspace
的策略,將核心邏輯與 WebAssembly 介面分離,這也正是許多大型專案的最佳實踐。非常感謝您的專業分享,期待您後續的文章!
也歡迎版主有空參考我的系列文「南桃AI重生記」:
https://ithelp.ithome.com.tw/users/20046160/ironman/8311